/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * GraphCanvas.java
 *
 * Created on Apr 17, 2009, 2:53:39 PM
 */
package view.panels.graphical;

import controller.GraphController;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import jgf.Arc;
import jgf.IGraph;
import jgf.Node;
import jgf.beans.ArcBean;
import jgf.beans.NodeBean;
import jgf.exceptions.arc.ArcException;
import jgf.exceptions.arc.BadArcInsertionException;
import jgf.exceptions.arc.UnexistentArcException;
import jgf.exceptions.attribute.BadAttributeListException;
import jgf.exceptions.node.DuplicatedNodeException;
import jgf.exceptions.node.UnexistentNodeException;
import view.panels.graphical.drawables.DrawArc;
import view.panels.graphical.drawables.DrawGraph;
import view.panels.graphical.drawables.DrawNode;
import view.panels.graphical.drawables.DrawSinkNode;
import view.panels.graphical.drawables.DrawSourceNode;
import view.panels.graphical.drawables.Interactive;

/**
 *
 * @author Marcos Garcia Ortega <thiamath@gmail.com>
 */
public class GraphCanvas extends JPanel {

    private GraphicalPanel father;
    private DrawGraph graph;

    /** Creates new form GraphCanvas */
    public GraphCanvas() {
        initComponents();
        lastClickCoords = new Point((int) this.getBounds().getCenterX(), (int) this.getBounds().getCenterY());
    }

    public void setFather(GraphicalPanel parent) {
        this.father = parent;
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        if (this.graph != null) {
            graph.display((Graphics2D) g);
        }
    }

    void loadGraph(DrawGraph graph) {
        this.graph = graph;
        this.formComponentResized(null);
        this.reshapeGraph();
    }

    public Rectangle getBounds2(Rectangle rv) {
        Rectangle ret = super.getBounds(rv);
        Collection<DrawNode> v = graph.getNodes();
        DrawNode rightest = null;
        DrawNode lowest = null;
        for (DrawNode drawNode : v) {
            if (rightest == null || rightest.getCenterX() < drawNode.getCenterX()) {
                rightest = drawNode;
            }
            if (lowest == null || lowest.getCenterY() < drawNode.getCenterY()) {
                lowest = drawNode;
            }
        }
        if (rightest == null || lowest == null) {
            return ret;
        }
        ret.setBounds((int) ret.getX(), (int) ret.getY(), (int) rightest.getCenterX(), (int) lowest.getCenterY());
        return ret;
    }

    void reshapeGraph() {
        this.graph.reshape();
        this.repaint();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        canvasNodePopup = new javax.swing.JPopupMenu();
        edit = new javax.swing.JMenuItem();
        toggleSource = new javax.swing.JMenuItem();
        toggleSink = new javax.swing.JMenuItem();
        editNode = new javax.swing.JDialog();
        jPanel1 = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        jTextField1 = new javax.swing.JTextField();
        jSeparator1 = new javax.swing.JSeparator();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTable1 = new javax.swing.JTable();
        editArc = new javax.swing.JDialog();

        edit.setText("Edit");
        edit.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                editActionPerformed(evt);
            }
        });
        canvasNodePopup.add(edit);

        toggleSource.setText("Toggle Source");
        toggleSource.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                toggleSourceActionPerformed(evt);
            }
        });
        canvasNodePopup.add(toggleSource);

        toggleSink.setText("Toggle Sink");
        toggleSink.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                toggleSinkActionPerformed(evt);
            }
        });
        canvasNodePopup.add(toggleSink);

        jPanel1.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));

        jLabel1.setText("jLabel1");
        jPanel1.add(jLabel1);

        jTextField1.setText("jTextField1");
        jPanel1.add(jTextField1);

        editNode.getContentPane().add(jPanel1, java.awt.BorderLayout.WEST);

        jSeparator1.setOrientation(javax.swing.SwingConstants.VERTICAL);
        editNode.getContentPane().add(jSeparator1, java.awt.BorderLayout.LINE_END);

        jScrollPane1.setPreferredSize(new java.awt.Dimension(25, 25));

        jTable1.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null, null},
                {null, null},
                {null, null},
                {null, null}
            },
            new String [] {
                "Attribute", "Value"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.String.class, java.lang.Integer.class
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }
        });
        jTable1.setPreferredSize(new java.awt.Dimension(62, 72));
        jScrollPane1.setViewportView(jTable1);
        jTable1.getColumnModel().getColumn(1).setResizable(false);

        editNode.getContentPane().add(jScrollPane1, java.awt.BorderLayout.CENTER);

        editNode.getAccessibleContext().setAccessibleParent(this);


        setBackground(new java.awt.Color(255, 255, 255));
        addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                formMouseClicked(evt);
            }
            public void mousePressed(java.awt.event.MouseEvent evt) {
                formMousePressed(evt);
            }
            public void mouseReleased(java.awt.event.MouseEvent evt) {
                formMouseReleased(evt);
            }
        });
        addComponentListener(new java.awt.event.ComponentAdapter() {
            public void componentResized(java.awt.event.ComponentEvent evt) {
                formComponentResized(evt);
            }
        });
        addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
            public void mouseDragged(java.awt.event.MouseEvent evt) {
                formMouseDragged(evt);
            }
        });

    }// </editor-fold>//GEN-END:initComponents
    private Point lastClickCoords;
	private void formMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseClicked
        lastClickCoords = evt.getPoint();
        GraphController gc = father.getController().getGraphController(graph.getID());
        Interactive pointed = graph.getPointed(lastClickCoords);
        switch (evt.getButton()) {
            case MouseEvent.BUTTON1:
                switch (father.getMode()) {
                    case GraphicalPanel.INNOCENT_MODE: // Select nodes and arcs
                        if (pointed != null) {
                            if (pointed.isSelected()) {
                                pointed.deselect();
                            } else {
                                pointed.select();
                            }
                        }
                        this.repaint();
                        break;
                    case GraphicalPanel.MANAGE_NODES: // Create nodes
						try {
							// Create nodes
							gc.insertNode(null);
						} catch (BadAttributeListException ex) {
							//TODO manage exception
						}

                        this.repaint();
                        break;
                    case GraphicalPanel.MANAGE_ARCS: // Do nothing
                        break;
                    case GraphicalPanel.ERASER_MODE:
                        if (pointed instanceof DrawArc) {
                            DrawArc arc = (DrawArc) pointed;
							ArcBean deletedArc = new ArcBean();
							deletedArc.setId(arc.getId());
							try {
								gc.deleteArc(deletedArc);
							} catch (UnexistentArcException ex) {
								//TODO manage exception
							}
                            this.repaint();
                        }
                        if (pointed instanceof DrawNode) {
                            DrawNode node = (DrawNode) pointed;
							NodeBean deletedNode = new NodeBean();
							deletedNode.setId(node.getId());
							try {
								gc.deleteNode(deletedNode);
							} catch (UnexistentNodeException ex) {
								//TODO manage exception
							}
                            this.repaint();
                        }
                        break;

                    default:
                }
                break;

            case MouseEvent.BUTTON2:
                break;
            case MouseEvent.BUTTON3:
                if (pointed instanceof DrawNode) {
                    canvasNodePopup.show(this, evt.getX(), evt.getY());
                }
                if (pointed instanceof DrawArc);
                break;
        }
	}//GEN-LAST:event_formMouseClicked

    // <editor-fold defaultstate="collapsed" desc="Drag & Drop Code">
    private Interactive dragged = null;

	private void formMouseDragged(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseDragged

//		switch (evt.getButton()) {
//			case MouseEvent.BUTTON1:
        switch (father.getMode()) {
            case GraphicalPanel.INNOCENT_MODE:
            case GraphicalPanel.MANAGE_NODES:
                Rectangle r = (Rectangle) this.getBounds().clone();
                r.setBounds(0, 0, this.getWidth(), this.getHeight());
                if (dragged != null && r.contains(evt.getPoint())) {// If an element is dragged
                    if (dragged instanceof DrawNode) {//If it is a node
                        dragged.setCenter(evt.getPoint());// Move it
                    }
                    this.repaint();
                }
                break;

            case GraphicalPanel.MANAGE_ARCS:
                if (dragged instanceof DrawNode) {
                    this.graph.moveFloatingArc(evt.getPoint());
                }
                this.repaint();
                break;

            case GraphicalPanel.ERASER_MODE:
                break;

            default:
        }
//				break;

//			case MouseEvent.BUTTON2:
//			case MouseEvent.BUTTON3:
//		}
	}//GEN-LAST:event_formMouseDragged

	private void formMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMousePressed

        if (evt.isPopupTrigger()) {
            lastClickCoords = evt.getPoint();
        }
        switch (evt.getButton()) {
            case MouseEvent.BUTTON1:
                switch (father.getMode()) {
                    case GraphicalPanel.INNOCENT_MODE:
                    case GraphicalPanel.MANAGE_NODES:
                        dragged = graph.getPointed(evt.getPoint());
                        break;

                    case GraphicalPanel.MANAGE_ARCS:
                        dragged = graph.getPointed(evt.getPoint());
                        if (dragged instanceof DrawNode) {
                            this.graph.setFloatingArc((DrawNode) dragged);
                            this.repaint();
                        }

                    case GraphicalPanel.ERASER_MODE:
                        break;

                    default:
                }
                break;

            case MouseEvent.BUTTON2:
            case MouseEvent.BUTTON3:
        }
	}//GEN-LAST:event_formMousePressed

	private void formMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseReleased
        switch (evt.getButton()) {
            case MouseEvent.BUTTON1:
                switch (father.getMode()) {
                    case GraphicalPanel.INNOCENT_MODE:
                    case GraphicalPanel.MANAGE_NODES:
                        if (dragged != null) {
                            dragged = null;
                            this.repaint();
                        }
                        break;

                    case GraphicalPanel.MANAGE_ARCS:
                        Interactive end = graph.getPointed(evt.getPoint());
                        if (dragged != null && dragged != end && end instanceof DrawNode) {
                            // From is "dragged"
                            // To is "end"
                            // The best way, I think, is to send the index of nodes.
                            GraphController gc = father.getController().getGraphController(graph.getID());
                            DrawNode from = (DrawNode) dragged;
                            DrawNode to = (DrawNode) end;
							ArcBean newArc = new ArcBean();
							newArc.setFromId(from.getId());
							newArc.setToId(to.getId());
                            try {
                                gc.insertArc(newArc);
                            } catch (BadArcInsertionException ex) {
                                JOptionPane.showMessageDialog(this, ex.getMessage());
                            } catch (UnexistentNodeException e) {
								//TODO manage exception
							} catch (DuplicatedNodeException e) {
								//TODO manage exception
							} catch(ArcException e) {
								//TODO manage exception
							}
                        }
                        graph.clearFloatingArc();
                        this.repaint();
                        break;

                    case GraphicalPanel.ERASER_MODE:
                        break;

                    default:
                }
                break;

            case MouseEvent.BUTTON2:
            case MouseEvent.BUTTON3:
        }
	}//GEN-LAST:event_formMouseReleased

	private void editActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editActionPerformed

        this.editNode.setVisible(true);
        this.editNode.pack();
	}//GEN-LAST:event_editActionPerformed

	private void toggleSourceActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_toggleSourceActionPerformed
		try {
			GraphController gc = father.getController().getGraphController(graph.getID());
			Interactive i = graph.getPointed(lastClickCoords);
			if (i instanceof DrawSourceNode) {
				DrawSourceNode node = (DrawSourceNode) i;
				NodeBean source = new NodeBean();
				source.setId(node.getId());
				gc.unsetSource(source);
			} else if (i instanceof DrawNode) {
				DrawNode node = (DrawNode) i;
				NodeBean source = new NodeBean();
				source.setId(node.getId());
				gc.setSource(source);
			}
		} catch (DuplicatedNodeException e){
			//TODO manage exception
		} catch (UnexistentNodeException e){
			//TODO manage exception
		}
        this.repaint();

}//GEN-LAST:event_toggleSourceActionPerformed

	private void toggleSinkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_toggleSinkActionPerformed
		try{
        GraphController gc = father.getController().getGraphController(graph.getID());
        Interactive i = graph.getPointed(lastClickCoords);
        if (i instanceof DrawSinkNode) {
            DrawSinkNode node = (DrawSinkNode) i;
			NodeBean sink = new NodeBean();
			sink.setId(node.getId());
            gc.unsetSink(sink);
        } else if (i instanceof DrawNode) {
            DrawNode node = (DrawNode) i;
			NodeBean sink = new NodeBean();
			sink.setId(node.getId());
            gc.setSink(sink);
        }
		} catch (DuplicatedNodeException e){
			//TODO manage exception
		} catch (UnexistentNodeException e){
			//TODO manage exception
		}
        this.repaint();

}//GEN-LAST:event_toggleSinkActionPerformed

	private void formComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentResized
        if (this.getHeight() >= this.getWidth()) {
            graph.setSpreadFactor(this.getWidth() / 2);
        } else {
            graph.setSpreadFactor(this.getHeight() / 2);
        }
        graph.setCenter(this.getWidth() / 2, this.getHeight() / 2);
	}//GEN-LAST:event_formComponentResized
    // </editor-fold>

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JPopupMenu canvasNodePopup;
    private javax.swing.JMenuItem edit;
    private javax.swing.JDialog editArc;
    private javax.swing.JDialog editNode;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JSeparator jSeparator1;
    private javax.swing.JTable jTable1;
    private javax.swing.JTextField jTextField1;
    private javax.swing.JMenuItem toggleSink;
    private javax.swing.JMenuItem toggleSource;
    // End of variables declaration//GEN-END:variables

    //<editor-fold defaultstate="collapsed" desc="Observer Cascade Recalls">
    //////////////////////////////
    // OBSERVER CASCADE RECALLS //
    //////////////////////////////
    public void insertArc(ArcBean arc) {
        this.graph.insertArc(arc);
    }

    public void deleteArc(ArcBean arc) {
        this.graph.deleteArc(arc);
    }

    public void updateArc(ArcBean arc) {
        this.graph.updateArc(arc);
    }

    public void colorArc(ArcBean arc, Color color) {
        this.graph.colorArc(arc, color);
    }

    public void insertNode(NodeBean node) {
        this.graph.insertNode(this.lastClickCoords, this.getVisibleRect(), node);
    }

    public void deleteNode(NodeBean node) {
        this.graph.deleteNode(node);
    }

    public void updateNode(NodeBean node) {
        this.graph.updateNode(node);
    }

    public void colorNode(NodeBean node, Color color) {
        this.graph.colorNode(node, color);
    }

    public void updateAll(IGraph graph) {
        this.graph = new DrawGraph(graph);
    }

    void setSource(NodeBean source) {
        this.graph.setSource(source);
    }

    void unsetSource(NodeBean source) {
        this.graph.unsetSource(source);
    }

    void setSink(NodeBean sink) {
        this.graph.setSink(sink);
    }

    void unsetSink(NodeBean sink) {
        this.graph.unsetSink(sink);
    }

	void addArcAttribute(String name, Integer defaultValue) {
		this.graph.addArcAttribute(name, defaultValue);
		this.repaint();
	}

	void updateArcAtribute(String previousName, String newName, Integer newValue) {
		this.graph.updateArcAttribute(previousName, newName, newValue);
		this.repaint();
	}

	void removeArcAttribute(String name) {
		this.graph.removeArcAttribute(name);
		this.repaint();
	}

	void addNodeAttribte(String name, Integer defaultValue) {
		this.graph.addNodeAttribute(name, defaultValue);
		this.repaint();
	}

	void updateNodeAttribute(String previousName, String newName, Integer newValue) {
		this.graph.updateNodeAttribute(previousName, newName, newValue);
		this.repaint();
	}

	void removeNodeAttribute(String name) {
		this.graph.removeNodeAttribute(name);
		this.repaint();
	}
    //</editor-fold>
}
